package com.nulldev.util.internal.testing;

import java.util.Map;
import java.util.concurrent.atomic.LongAdder;

import com.nulldev.util.JVM.Exceptions;
import com.nulldev.util.JVM.JVM;
import com.nulldev.util.VariableAPI.RandomUtil;
import com.nulldev.util.data.Arrays.maps.FastMaps;

public class TestManager implements Runnable {

	private final Map<String, Test<?>> TESTS = FastMaps.map();

	public TestManager() {
	}

	public TestManager(final Map<String, Test<?>> tests) {
		this.TESTS.putAll(tests);
	}

	@Override
	public void run() {
		JVM.println("Running " + this.TESTS.size() + " test(s).");
		final LongAdder passedTests = new LongAdder();
		final LongAdder failedTests = new LongAdder();
		final LongAdder crashedTests = new LongAdder();
		final LongAdder disabledTests = new LongAdder();
		final LongAdder warnings = new LongAdder();
		for (final Map.Entry<String, Test<?>> test : this.TESTS.entrySet()) {
			JVM.println("\t\t\t========= " + test.getKey() + " =========");
			test.getValue().run();
			JVM.println(test.getKey() + ": " + test.getValue().getResult());
			if (test.getValue().getFailReason() != null) {
				switch (test.getValue().getFailReason()) {
					case VALUE_MISMATCH: {
						JVM.println("\t\t(" + test.getValue().getFailReason() + ")");
						JVM.println("\t\t(Expected: " + test.getValue().getExpectedResultItem() + ")");
						JVM.println("\t\t(Got: " + test.getValue().getResultItem() + ")");
						break;
					}
					default: {
						JVM.println("\t\t(" + test.getValue().getFailReason() + ")");
						break;
					}
				}
			}
			switch (test.getValue().getResult()) {
				case QUEUED:
					break; // TODO: Add a didn't run counter.
				case PASSED_WITH_WARNINGS:
					warnings.increment();
				case PASSED: {
					passedTests.increment();
					break;
				}
				case FAILED: {
					failedTests.increment();
					break;
				}
				case CRASHED: {
					crashedTests.increment();
					break;
				}
				case DISABLED: {
					disabledTests.increment();
					break;
				}
			}
			try {
				Thread.sleep(RandomUtil.randLong(200, 4500));
			} catch (InterruptedException e) {
			}
		}
		JVM.println("Tests finished.");
		JVM.println("Results: ");
		JVM.println("\tPassed: " + passedTests.sum());
		JVM.println("\tWarnings: " + warnings.sum());
		JVM.println("\tCrashed: " + crashedTests.sum());
		JVM.println("\tFailed: " + failedTests.sum());
		JVM.println("\tDisabled: " + disabledTests.sum());
		if (failedTests.sum() > 0) {
			JVM.println("Printing failed tests.");
			for (final Map.Entry<String, Test<?>> test : this.TESTS.entrySet()) {
				final Test<?> t = test.getValue();
				if (t.getResult() == TestResult.FAILED && t.hasError()) {
					JVM.println(test.getKey() + ":");
					JVM.println(Exceptions.toString(t.getError()));
				} else if (t.getResult() == TestResult.FAILED && !t.hasError()) {
					JVM.println(test.getKey() + " - No error provided.");
				}
			}
		}
		if (crashedTests.sum() > 0) {
			JVM.println("Printing crashed tests.");
			for (final Map.Entry<String, Test<?>> test : this.TESTS.entrySet()) {
				final Test<?> t = test.getValue();
				if (t.getResult() == TestResult.CRASHED && t.hasError()) {
					JVM.println(test.getKey() + ":");
					JVM.println(Exceptions.toString(t.getError()));
				}
			}
		}
	}
}
